/**
 * notifyAll()的困惑问题
 *
 * @author fireway
 * @since 2019年 02月 20日 星期三 07:25:46 CST
 */
package concurrency;

import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class Task implements Runnable {
    static Blocker sBlocker = new Blocker();

    @Override
    public void run() {
        System.out.println(this + " wait in " + Thread.currentThread());
        sBlocker.waitingCall(this);
    }
}

class Task2 implements Runnable {
    static Blocker sBlocker = new Blocker();

    @Override
    public void run() {
        System.out.println(this + " wait in " + Thread.currentThread());
        sBlocker.waitingCall(this);
    }
}

class Blocker {
    synchronized void waitingCall(Runnable task) {
        try {
            while (!Thread.interrupted()) {
                wait();
                System.out.print(Thread.currentThread() + ", ");
            }
        } catch (InterruptedException e) {
            System.out.println(task + " waitingCall occur InterruptedException");
        }
    }

    synchronized void prod() {
        notify();
    }

    synchronized void prodAll() {
        notifyAll();
    }
}

public class NotifyVsNotifyAll {
    public static void main(String[] args) {
        ExecutorService exec = Executors.newCachedThreadPool();

        for(int i = 0; i < 5; i++) {
            exec.execute(new Task());
        }
        exec.execute(new Task2());

        Timer timer = new Timer();
        // Run every 0.4 second
        timer.scheduleAtFixedRate(new TimerTask() {
            boolean mProd = true;

            @Override
            public void run() {
                if(mProd) {
                    System.out.print(Thread.currentThread() + " notify() ");
                    Task.sBlocker.prod();
                    mProd = false;
                } else {
                    System.out.print(Thread.currentThread() + " notifyAll() ");
                    Task.sBlocker.prodAll();
                    mProd = true;
                }
            }
        }, 400, 400);

        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        timer.cancel();
        System.out.println("\nTimer canceled");

        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.print("Task2.blocker.prodAll() ");
        Task2.sBlocker.prodAll();

        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("\nShutting down");

        exec.shutdownNow();
    }
}
